Le kernel OCaml n'est pas installé par défaut avec Jupyter.
Il faut installer OPAM, puis ocaml-jupyter.
Sys.command "ocaml -version";;
The OCaml toplevel, version 4.05.0
- : int = 0
print_endline "Bonjour depuis OCaml !";;
Bonjour depuis OCaml !
- : unit = ()
Pour calculer la fonction $n! := 1 \times 2 \times \dots \times n$ ($n\in\mathbb{N}$), on peut penser à une solution récursive (qui coutera en espace mémoire à cause de la pile d'appel) et une solution impérative.
let rec fact (n: int) : int =
match n with
| 0 -> 1
| n -> n * (fact (n-1))
;;
val fact : int -> int = <fun>
for i = 1 to 21 do
print_endline (Printf.sprintf "fact(%.2i) = %i" i (fact i))
done;;
fact(01) = 1 fact(02) = 2 fact(03) = 6 fact(04) = 24 fact(05) = 120 fact(06) = 720 fact(07) = 5040 fact(08) = 40320 fact(09) = 362880 fact(10) = 3628800 fact(11) = 39916800 fact(12) = 479001600 fact(13) = 6227020800 fact(14) = 87178291200 fact(15) = 1307674368000 fact(16) = 20922789888000 fact(17) = 355687428096000 fact(18) = 6402373705728000 fact(19) = 121645100408832000 fact(20) = 2432902008176640000 fact(21) = -4249290049419214848
- : unit = ()
Et la solution impérative :
let fact_imp (n: int) : int =
let f = ref 1 in
for i = 1 to n do
f := (!f) * i;
done;
!f
;;
val fact_imp : int -> int = <fun>
for i = 15 to 21 do
print_endline (Printf.sprintf "fact(%.2i) = %i" i (fact_imp i))
done;;
fact(15) = 1307674368000 fact(16) = 20922789888000 fact(17) = 355687428096000 fact(18) = 6402373705728000 fact(19) = 121645100408832000 fact(20) = 2432902008176640000 fact(21) = -4249290049419214848
- : unit = ()
Comme les entiers sont bornés, on dépasse la capacité assez rapidement, et les valeurs calculées deviennent fausses.
type formulePropositionnelle =
| Var of string
| Non of formulePropositionnelle
| Ou of (formulePropositionnelle * formulePropositionnelle)
| Et of (formulePropositionnelle * formulePropositionnelle)
;;
type formulePropositionnelle = Var of string | Non of formulePropositionnelle | Ou of (formulePropositionnelle * formulePropositionnelle) | Et of (formulePropositionnelle * formulePropositionnelle)
let x = Var("x")
and y = Var("y")
and z = Var("z")
;;
val x : formulePropositionnelle = Var "x" val y : formulePropositionnelle = Var "y" val z : formulePropositionnelle = Var "z"
let p1 = Ou(x, y)
and p2 = Et(y, z)
and p3 = Non(x)
;;
let p4 = Et(p1, p2);;
let p5 = Ou(p3, p4);;
let p6 = Non(p5);;
val p1 : formulePropositionnelle = Ou (Var "x", Var "y") val p2 : formulePropositionnelle = Et (Var "y", Var "z") val p3 : formulePropositionnelle = Non (Var "x")
val p4 : formulePropositionnelle = Et (Ou (Var "x", Var "y"), Et (Var "y", Var "z"))
val p5 : formulePropositionnelle = Ou (Non (Var "x"), Et (Ou (Var "x", Var "y"), Et (Var "y", Var "z")))
val p6 : formulePropositionnelle = Non (Ou (Non (Var "x"), Et (Ou (Var "x", Var "y"), Et (Var "y", Var "z"))))
let rec taille (formule: formulePropositionnelle) : int =
match formule with
| Var _ -> 1
| Non phi -> 1 + taille phi
| Et (phi1, phi2) | Ou (phi1, phi2) -> 1 + (taille phi1) + (taille phi2)
(* | _ -> 0 *) (* cette ligne est inutile *)
;;
val taille : formulePropositionnelle -> int = <fun>
taille x;;
taille y;;
taille z;;
- : int = 1
- : int = 1
- : int = 1
taille p1;;
taille p2;;
taille p3;;
- : int = 3
- : int = 3
- : int = 2
Avec une fonction récursive terminale :
let taille (formule: formulePropositionnelle) : int =
let rec aux acc = function
| Var _ -> 1
| Non phi -> aux (acc+1) phi
| Et (phi1, phi2) | Ou (phi1, phi2) -> aux (aux (acc + 1) phi1) phi2
in aux 0 formule
;;
val taille : formulePropositionnelle -> int = <fun>
taille p4;;
taille p5;;
taille p6;;
- : int = 1
- : int = 1
- : int = 1
type 'a arbreBinaire = Feuille | Noeud of ('a arbreBinaire * 'a * 'a arbreBinaire);;
type 'a arbreBinaire = Feuille | Noeud of ('a arbreBinaire * 'a * 'a arbreBinaire)
let a0 = Feuille;;
(*
1
/ \
F F
*)
let a1 = Noeud(a0, 1, a0);;
(*
2
/ \
1 F
/ \
F F
*)
let a2 = Noeud(a1, 2, a0);;
val a0 : 'a arbreBinaire = Feuille
val a1 : int arbreBinaire = Noeud (Feuille, 1, Feuille)
val a2 : int arbreBinaire = Noeud (Noeud (Feuille, 1, Feuille), 2, Feuille)
let rec hauteur (arbre: 'a arbreBinaire) : int =
match arbre with
| Feuille -> 0
| Noeud(g, _, d) -> 1 + max (hauteur g) (hauteur d)
;;
val hauteur : 'a arbreBinaire -> int = <fun>
hauteur a0;;
hauteur a1;;
hauteur a2;;
- : int = 0
- : int = 1
- : int = 2
print_string "Hello world from jupyter-ocaml";;
- : unit = ()
flush_all();;
- : unit = ()
print_endline "Hello world from jupyter-ocaml";;
Hello world from jupyter-ocamlHello world from jupyter-ocaml
- : unit = ()
Ce comportement est assez perturbant avec jupyter-ocaml
, cf ce ticket.
print_endline "Hello world from jupyter-ocaml";;
Hello world from jupyter-ocaml
- : unit = ()
Avec Printf.printf
, c'est encore plus facile :
let print = Printf.printf ;;
val print : ('a, out_channel, unit) format -> 'a = <fun>
print "Hello world from jupyter-ocaml\n%d + %d = %d\n" 1 2 (1 + 2);;
- : unit = ()
flush_all();;
Hello world from jupyter-ocaml 1 + 2 = 3
- : unit = ()
Avec une fonction autour de Printf.printf
, c'est encore encore plus facile :
let print s =
print_endline s ;
flush_all ()
;;
val print : string -> unit = <fun>
for _ = 1 to 3 do
print (Printf.sprintf "Hello world from jupyter-ocaml\n%d + %d = %d" 1 2 (1 + 2))
done;;
Hello world from jupyter-ocaml 1 + 2 = 3 Hello world from jupyter-ocaml 1 + 2 = 3 Hello world from jupyter-ocaml 1 + 2 = 3
- : unit = ()
%load_ext itikz
File "[1]", line 1, characters 0-1: Error: Syntax error 1: %load_ext itikz
Il n'est pas encore possible d'utiliser des "magic commands" de IPython dans d'autres langages. J'ai ouvert ces tickets : #164 et #165 sur le projet ocaml-jupyter sur GitHub.
Une solution intermédiaire, et assez facile d'utilisation en fait, est de temporairement changer le "kernel" pour utiliser Python, générer les figures, puis repasser à OCaml !
%load_ext itikz
%%itikz --file-prefix tikz-figures-from-OCaml- --implicit-pic
\draw[help lines] grid (5, 5);
\draw[fill=magenta!10] (1, 1) rectangle (2, 2);
\draw[fill=magenta!10] (2, 1) rectangle (3, 2);
\draw[fill=magenta!10] (3, 1) rectangle (4, 2);
\draw[fill=magenta!10] (3, 2) rectangle (4, 3);
\draw[fill=magenta!10] (2, 3) rectangle (3, 4);
%%itikz --file-prefix tikz-figures-from-OCaml- --implicit-pic --scale=0.4
\tikzstyle{vertexcover} = [circle,fill=green,draw];
\tikzstyle{matching} = [ draw=blue!55, line width=5];
\tikzstyle{matchingN} = [ draw=green!55, line width=5];
\tikzstyle{vertex} = [circle,fill=none,draw];
\node[vertex] (v2) at (0,0) {};
\node[vertexcover] (v3) at (0,3) {};
\node[vertexcover] (v4) at (2,2) {};
\node[vertexcover] (v11) at (3,0) {};
\node[vertexcover] (v5) at (2,-2) {};
\node[vertexcover] (v6) at (1,-3) {};
\node[vertexcover] (v8) at (-1,-3) {};
\node[vertexcover] (v9) at (-2,-2) {};
\node[vertexcover] (v10) at (-3,0) {};
\node[vertexcover] (v1) at (-2,2) {};
\draw (v1) edge (v2);
\draw (v3) edge (v2);
\draw (v4) edge (v2);
\draw (v5) edge (v2);
\draw (v6) edge (v2);
\draw (v8) edge (v2);
\draw (v9) edge (v2);
\draw (v2) edge (v10);
\draw (v2) edge (v11);
!ls tikz-figures-from-OCaml-*
tikz-figures-from-OCaml-c33720202396643581aed22f595383d4.svg tikz-figures-from-OCaml-c33720202396643581aed22f595383d4.tex tikz-figures-from-OCaml-f9a50e84b7fbf18e85525cc31a91a070.svg tikz-figures-from-OCaml-f9a50e84b7fbf18e85525cc31a91a070.tex
Les commandes IPython magic, comme utilisées ci-dessus (en basculant au kernel Python pour ces quelques cellules) sont très pratiques.
Un autre avantage sont les raccourcis, comme ceux là :
# cette cellule doit être exécutée avec le kernel Python
!ls *OCaml*nb
!pwd
'Exemple de notebook avec OCaml.ipynb' /home/lilian/publis/Info-Prepas-MP2I/Modele-de-livre-avec-Jupyter-Book.git/notebooks
Pour certains commandes basiques, peut-être qu'on peut tricher un peu ?
let (! ) s =
(* Sys.command ("echo "^s); *)
Sys.command (s)
;;
val ( ! ) : string -> int = <fun>
!"ls *OCaml*nb";;
!"pwd";;
Exemple de notebook avec OCaml.ipynb
- : int = 0
/home/lilian/publis/Info-Prepas-MP2I/Modele-de-livre-avec-Jupyter-Book.git/notebooks
- : int = 0
On pourrait envisager de faire pareil pour d'autres "cell magics", en ouvrant un terminal IPython sous-jacent, avec Sys.command
, et en lui envoyant le code de la cellule, puis en récupérant la sortie, et afficher la sortie comme une sortie normale, avec Jupyter_notebook.display
.
Je ne pense pas avoir le temps de bidouiller tout ça...
TODO: plus tard !